#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

//1、请设计一个类，不能被拷贝
class CopyBanCpp98 {
    //将拷贝构造函数与赋值运算符重载只声明不定义，并且将其访问权限设置为私有即可
private:
    CopyBanCpp98(const CopyBanCpp98 &);
    CopyBanCpp98 &operator=(const CopyBanCpp98 &);
};
class CopyBanCpp11 {
    //C++11扩展delete的用法，delete除了释放new申请的资源外，如果在默认成员函数后跟上
    //=delete，表示让编译器删除掉该默认成员函数
    CopyBanCpp11(const CopyBanCpp11 &) = delete;
    CopyBanCpp11 &operator=(const CopyBanCpp11 &) = delete;
};

//2. 请设计一个类，只能在堆上创建对象
//实现方式：
//1. 将类的构造函数私有，拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
//2. 提供一个静态的成员函数，在该静态成员函数中完成堆对象的创建
class HeapOnly {
public:
    static HeapOnly *CreateObject()//这样只能调用 HeapOnly::CreateObject() 来new一个对象
    {
        return new HeapOnly;
    }
private:
    HeapOnly() {}
    // C++98
    // 1.只声明,不实现。因为实现可能会很麻烦，而你本身不需要
    // 2.声明成私有
    HeapOnly(const HeapOnly &) = delete;
};

//3. 请设计一个类，只能在栈上创建对象
//和上面一样，调用的CreateObj只能用普通构造来创建对象
class StackOnly {
public:
    static StackOnly CreateObj() {
        return StackOnly();
    }
    // 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
    // StackOnly obj = StackOnly::CreateObj();
    // StackOnly* ptr3 = new StackOnly(obj);
    void *operator new(size_t size) = delete;
    void operator delete(void *p) = delete;
private:
    StackOnly()
            : _a(0) {}
private:
    int _a;
};

//4. 请设计一个类，不能被继承
class NonInherit//final  在类名后加上final也无法被继承：C++11
{
public:
    static NonInherit GetInstance() {
        return NonInherit();
    }
private:
    NonInherit()//私有构造函数就不能被继承了
    {}
};

//单例模式
//1、饿汉模式
//class Singleton {
//public:
//    static Singleton *GetInstance() {
//        return &m_instance;
//    }
//private:
//    // 构造函数私有
//    Singleton() {};
//    Singleton(const Singleton  &s) = delete;
//    Singleton &operator=(const Singleton &s) = delete;
//    static Singleton m_instance;//单例对象，只有这一个对象，无法创建其它对象
//};
//Singleton Singleton::m_instance;  // 在程序入口之前就完成单例对象的初始化

//2、懒汉模式
class Singleton {
public:
    static Singleton *GetInstance() {
        // 双检查加锁
        if (_ins == nullptr) {
            _imtx.lock();

            if (_ins == nullptr) {
                _ins = new Singleton;
            }

            _imtx.unlock();
        }

        return _ins;
    }

    // 一般全局都要使用单例对象，所以单例对象一般不需要显示释放
    // 有些特殊场景，想显示释放一下
    static void DelInstance() {
        _imtx.lock();
        if (_ins) {
            delete _ins;
            _ins = nullptr;
        }
        _imtx.unlock();
    }

    // 单例对象回收
    class GC {
    public:
        ~GC() {
            DelInstance();
        }
    };

    static GC _gc;

    void Add(const string &str) {
        _vmtx.lock();

        _v.push_back(str);

        _vmtx.unlock();
    }

    void Print() {
        _vmtx.lock();

        for (auto &e : _v) {
            cout << e << endl;
        }
        cout << endl;

        _vmtx.unlock();
    }

    ~Singleton() {
        // 持久化
        // 比如要求程序结束时，将数据写到文件，单例对象析构时持久化就比较好
    }

private:
    // 限制类外面随意创建对象
    Singleton() {}
    Singleton(const Singleton& s)=delete;
    Singleton& operator=(const Singleton& s)=delete;
private:
    mutex _vmtx;
    vector<string> _v;

    static Singleton *_ins;
    static mutex _imtx;
};

Singleton *Singleton::_ins = nullptr;
mutex Singleton::_imtx;

Singleton::GC Singleton::_gc;

int main() {
    srand(time(0));

    int n = 30;
    thread t1([n]() {
        for (size_t i = 0; i < n; ++i) {
            Singleton::GetInstance()->Add("t1线程:" + to_string(rand()));
        }
    });

    thread t2([n]() {
        for (size_t i = 0; i < n; ++i) {
            Singleton::GetInstance()->Add("t2线程:" + to_string(rand()));
        }
    });

    t1.join();
    t2.join();

    Singleton::GetInstance()->Print();

    return 0;
}